﻿using System;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.ServiceModel;
using System.Threading;

namespace N3P.WCF.Proxyless
{
    /// <summary>
    /// Shared resources for the WCF contract converter
    /// </summary>
    [DebuggerNonUserCode]
    internal static class SharedResources
    {

        /// <summary>
        /// The assembly being used to generate the dynamic WCF types for the the current application
        /// </summary>
        private static readonly AssemblyBuilder Assembly;
        /// <summary>
        /// The type of the <see cref="T:System.AsyncCallback" /> delegate
        /// </summary>
        public static readonly Type AsyncCallbackType;
        /// <summary>
        /// The type of <see cref="T:System.IAsyncResult" />
        /// </summary>
        public static readonly Type AsyncResultType;
        /// <summary>
        /// An action that does nothing
        /// </summary>
        public static readonly Action DefaultAction;
        /// <summary>
        /// An action that invokes an action on the UI thread
        /// </summary>
        public static readonly Action<Action, bool> InvokeOnUIThread;
        /// <summary>
        /// The type of <see cref="T:System.Object" />
        /// </summary>
        public static readonly Type ObjectType;
        /// <summary>
        /// The <see cref="P:System.ServiceModel.OperationContractAttribute.Action" /> property of <see cref="T:System.ServiceModel.OperationContractAttribute" />
        /// </summary>
        public static readonly PropertyInfo OperationContractActionProperty;
        /// <summary>
        /// The <see cref="P:System.ServiceModel.OperationContractAttribute.AsyncPattern" /> property of <see cref="T:System.ServiceModel.OperationContractAttribute" />
        /// </summary>
        public static readonly PropertyInfo OperationContractAsyncPatternProperty;
        /// <summary>
        /// The default constructor for <see cref="T:System.ServiceModel.OperationContractAttribute" />
        /// </summary>
        public static readonly ConstructorInfo OperationContractAttributeCtor;
        /// <summary>
        /// The type of <see cref="T:System.ServiceModel.OperationContractAttribute" />
        /// </summary>
        public static readonly Type OperationContractAttributeType;
        /// <summary>
        /// The <see cref="P:System.ServiceModel.OperationContractAttribute.ReplyAction" /> property of <see cref="T:System.ServiceModel.OperationContractAttribute" />
        /// </summary>
        public static readonly PropertyInfo OperationContractReplyActionProperty;
        /// <summary>
        /// The <see cref="T:System.Reflection.PropertyInfo" /> for the <see cref="P:System.ServiceModel.ServiceContractAttribute.ConfigurationName" /> property on <see cref="T:System.ServiceModel.ServiceContractAttribute" />
        /// </summary>
        public static readonly PropertyInfo ServiceContractAttributeConfigurationNameProperty;
        /// <summary>
        /// The <see cref="T:System.Reflection.ConstructorInfo" /> for the default constructor for <see cref="T:System.ServiceModel.ServiceContractAttribute" />
        /// </summary>
        public static readonly ConstructorInfo ServiceContractAttributeContructor;
        /// <summary>
        /// The <see cref="T:System.Reflection.PropertyInfo" /> for the <see cref="P:System.ServiceModel.ServiceContractAttribute.Name" /> property on <see cref="T:System.ServiceModel.ServiceContractAttribute" />
        /// </summary>
        public static readonly PropertyInfo ServiceContractAttributeNameProperty;
        /// <summary>
        /// The <see cref="T:System.Reflection.PropertyInfo" /> for the <see cref="P:System.ServiceModel.ServiceContractAttribute.Namespace" /> property on <see cref="T:System.ServiceModel.ServiceContractAttribute" />
        /// </summary>
        public static readonly PropertyInfo ServiceContractAttributeNamespaceProperty;
        /// <summary>
        /// The type of <see cref="T:System.ServiceModel.ServiceContractAttribute" />
        /// </summary>
        public static readonly Type ServiceContractAttributeType;
        /// <summary>
        /// http://tempuri.org
        /// </summary>
        public const string TempUri = @"http://tempuri.org";



        /// <summary>
        /// Initializes the shared resource set
        /// </summary>
        static SharedResources()
        {
            var assemblyName = "WCF" + DateTime.Now.Ticks;
            var asmName = new AssemblyName(assemblyName);
            Assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
            Module = Assembly.DefineDynamicModule(assemblyName);

            OperationContractAttributeType = typeof(OperationContractAttribute);
            OperationContractAttributeCtor = OperationContractAttributeType.GetConstructor(Type.EmptyTypes);
            OperationContractAsyncPatternProperty = OperationContractAttributeType.GetProperty("AsyncPattern");
            OperationContractActionProperty = OperationContractAttributeType.GetProperty("Action");
            OperationContractReplyActionProperty = OperationContractAttributeType.GetProperty("ReplyAction");

            ServiceContractAttributeType = typeof(ServiceContractAttribute);
            ServiceContractAttributeContructor = ServiceContractAttributeType.GetConstructor(Type.EmptyTypes);
            ServiceContractAttributeNameProperty = ServiceContractAttributeType.GetProperty("Name");
            ServiceContractAttributeConfigurationNameProperty = ServiceContractAttributeType.GetProperty("ConfigurationName");
            ServiceContractAttributeNamespaceProperty = ServiceContractAttributeType.GetProperty("Namespace");

            AsyncResultType = typeof(IAsyncResult);
            AsyncCallbackType = typeof(AsyncCallback);
            ObjectType = typeof(object);

            DefaultAction = () => { };

            InvokeOnUIThread = GenerateInvokeOnUIThreadMethod();
        }



        /// <summary>
        /// Gets the <see cref="T:System.Reflection.Emit.ModuleBuilder" /> that types should be defined in when defining additional WCF related items
        /// </summary>
        internal static ModuleBuilder Module { get; private set; }




        /// <summary>
        /// Generates the method that invokes the callback on the UI thread
        /// </summary>
        /// <returns>The method that invokes the callback on the UI thread</returns>
        private static Action<Action, bool> GenerateInvokeOnUIThreadMethod()
        {
            var originalContext = SynchronizationContext.Current;

            if (originalContext != null)
            {
                return (act, skip) =>
                {
                    if (!skip)
                    {
                        originalContext.Post(obj => act(), null);
                    }

                    act();
                };
            }

            return (act, skip) => act();
        }
    }
}
